package org.hcyspartnoc.luoxin.service.system.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.hcyspartnoc.luoxin.common.core.PageBean;
import org.hcyspartnoc.luoxin.common.core.enums.CoreSysRole;
import org.hcyspartnoc.luoxin.common.enums.system.SysRoleStatusEnum;
import org.hcyspartnoc.luoxin.common.exception.system.SysRoleException;
import org.hcyspartnoc.luoxin.mapper.system.SysRoleMapper;
import org.hcyspartnoc.luoxin.mapper.system.SysRolePermissionMapper;
import org.hcyspartnoc.luoxin.mapper.system.SysRoleUserMapper;
import org.hcyspartnoc.luoxin.pojo.dto.system.SysRoleAddDto;
import org.hcyspartnoc.luoxin.pojo.dto.system.SysRoleModifyDto;
import org.hcyspartnoc.luoxin.pojo.po.system.SysRolePo;
import org.hcyspartnoc.luoxin.pojo.po.system.SysRoleUserPo;
import org.hcyspartnoc.luoxin.pojo.vo.system.SysRoleVo;
import org.hcyspartnoc.luoxin.service.system.ISysRoleService;
import org.hcyspartnoc.luoxin.utils.StringUtils;
import org.hcyspartnoc.luoxin.utils.redis.RedisComponent;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;

@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRolePo> implements ISysRoleService {
    private static final ArrayList<CoreSysRole> ALL_CORE_ROLE = new ArrayList<>();

    static {
        Collections.addAll(ALL_CORE_ROLE, CoreSysRole.values());
    }

    @Resource
    private SysRoleMapper sysRoleMapper;
    @Resource
    private SysRoleUserMapper sysRoleUserMapper;
    @Resource
    private SysRolePermissionMapper sysRolePermissionMapper;
    @Resource
    private RedisComponent redisComponent;

    @Override
    @Transactional
    public SysRoleVo addRole(SysRoleAddDto sysRoleAddDto) throws SysRoleException {
        // 校验该角色名称是否已经存在
        roleExistsThrow(sysRoleAddDto.getName());
        // 插入角色
        SysRolePo sysRolePo = BeanUtil.copyProperties(sysRoleAddDto, SysRolePo.class);
        save(sysRolePo);
        return rolePoToVo(sysRoleMapper.selectOne(new LambdaQueryWrapper<>(SysRolePo.class).eq(SysRolePo::getId, sysRolePo.getId())));
    }

    @Override
    @Transactional
    public SysRoleVo modifyRole(SysRoleModifyDto sysRoleModifyDto) throws SysRoleException {
        // 检查被修改的是否是禁止操作的角色
        if (ALL_CORE_ROLE.contains(CoreSysRole.getById(sysRoleModifyDto.getId())))
            throw new SysRoleException(SysRoleException.ROLE_COULD_NOT_ACT);
        // 检查id是否存在
        roleNotExistsThrow(sysRoleModifyDto.getId());
        // 检查新名称是否重复
        SysRolePo savedRolePo = sysRoleMapper.selectOne(new LambdaQueryWrapper<>(SysRolePo.class).eq(SysRolePo::getName, sysRoleModifyDto.getName()));
        // 如果新昵称已经被别的角色占用
        if (Objects.nonNull(savedRolePo))
            if (!Objects.equals(savedRolePo.getId(), sysRoleModifyDto.getId()))
                throw new SysRoleException(SysRoleException.ROLE_HAS_EXISTS);
        // 修改角色信息
        SysRolePo sysRolePo = BeanUtil.copyProperties(sysRoleModifyDto, SysRolePo.class);
        saveOrUpdate(sysRolePo);
        return rolePoToVo(sysRoleMapper.selectOne(new LambdaQueryWrapper<>(SysRolePo.class).eq(SysRolePo::getId, sysRolePo.getId())));
    }

    @Override
    @Transactional
    public void removeRole(Integer roleId) throws SysRoleException {
        // 角色为核心角色异常
        if (ALL_CORE_ROLE.contains(CoreSysRole.getById(roleId)))
            throw new SysRoleException(SysRoleException.ROLE_COULD_NOT_REMOVE);
        // 角色不存在异常
        roleNotExistsThrow(roleId);
        // 换绑用户角色为普通角色
        sysRoleUserMapper.update(new LambdaUpdateWrapper<>(SysRoleUserPo.class)
                .eq(SysRoleUserPo::getRoleId, roleId)
                .set(SysRoleUserPo::getRoleId, CoreSysRole.NORMAL_USER.getId()));
        // 删除角色
        removeById(roleId);
    }

    @Override
    public PageBean<SysRolePo, SysRoleVo> queryByPage(Integer currentPage, Integer pageSize, String name, Integer status) {
        LambdaQueryWrapper<SysRolePo> wrapper = new LambdaQueryWrapper<>(SysRolePo.class)
                .like(Objects.nonNull(name) && !name.isEmpty(), SysRolePo::getName, name)
                .eq(status != 0, SysRolePo::getStatus, status)
                .orderByAsc(SysRolePo::getId);
        Page<SysRolePo> page = new Page<>(currentPage, pageSize);
        sysRoleMapper.selectPage(page, wrapper);
        ArrayList<SysRoleVo> sysRoleVos = new ArrayList<>();
        for (SysRolePo sysRolePo : page.getRecords()) {
            sysRoleVos.add(rolePoToVo(sysRolePo));
        }
        return new PageBean<>(page, sysRoleVos);
    }

    @Override
    @Transactional
    public void modifyStatus(Integer roleId) throws SysRoleException {
        // 超级管理员不允许修改
        if (CoreSysRole.SUPER_ADMIN.getId().equals(roleId))
            throw new SysRoleException(SysRoleException.ROLE_COULD_NOT_ACT);
        // 角色不存在异常
        SysRolePo sysRolePo = roleNotExistsThrow(roleId);
        // 修改状态
        SysRoleStatusEnum newStatus = SysRoleStatusEnum.USABLE;
        if (Objects.requireNonNull(SysRoleStatusEnum.getByCode(sysRolePo.getStatus())).equals(SysRoleStatusEnum.USABLE))
            newStatus = SysRoleStatusEnum.DISABLE;
        sysRoleMapper.update(new LambdaUpdateWrapper<>(SysRolePo.class)
                .set(SysRolePo::getStatus, newStatus.getCode())
                .eq(SysRolePo::getId, roleId));
        // 修改redis状态
        redisComponent.setRole(roleId, newStatus.getCode());
    }

    private SysRoleVo rolePoToVo(SysRolePo sysRolePo) {
        SysRoleVo sysRoleVo = BeanUtil.copyProperties(sysRolePo, SysRoleVo.class);
        sysRoleVo.setFormatCreateTime(StringUtils.formatDateTime(sysRolePo.getCreateTime()));
        return sysRoleVo;
    }

    private void roleExistsThrow(String name) throws SysRoleException {
        if (sysRoleMapper.exists(new LambdaQueryWrapper<>(SysRolePo.class).eq(SysRolePo::getName, name)))
            throw new SysRoleException(SysRoleException.ROLE_HAS_EXISTS);
    }

    private SysRolePo roleNotExistsThrow(Integer roleId) throws SysRoleException {
        SysRolePo sysRolePo = sysRoleMapper.selectOne(new LambdaQueryWrapper<>(SysRolePo.class).eq(SysRolePo::getId, roleId));
        if (Objects.isNull(sysRolePo))
            throw new SysRoleException(SysRoleException.ROLE_NOT_EXISTS);
        return sysRolePo;
    }

}
